package com.duoduo.common.api.help;


import com.duoduo.common.api.constants.Constant;
import com.duoduo.common.api.constants.LogConstant;
import com.duoduo.common.api.exception.lock.AddLockException;
import com.duoduo.common.api.exception.lock.ReleaseLockException;
import com.duoduo.common.api.exception.lock.UnrerentLockException;
import com.duoduo.common.api.myinterface.LockOperator;
import com.duoduo.common.api.spi.extension.ExtensionLoader;
import com.duoduo.common.api.tuple.TwoTuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**分布式锁操作接口
 * @author duoduo
 *
 */
public class LockOperatorInnerUtil {
	
	private static Logger logger = LoggerFactory.getLogger(LockOperatorInnerUtil.class);
	private static String cacheType = null;
	private static LockOperator lockOperator = null;
	static{
		try{
			//分布式锁的具体实现工具，默认是redis
			cacheType = SpringContextInnerUtil.get(Constant.LOCK_TYPE);
			lockOperator = ExtensionLoader.getExtensionLoader(LockOperator.class).getExtension(cacheType);
		}catch(Exception e){
			logger.error(LogConstant.DISTRIBUTE_LOCK + " init lock error", e);
		}
	}
	
	/**
     * 尝试获取分布式锁，直接使用RedisUtil实现
     * @param prefixName 当配置了多个集群时，可以指定使用哪个前缀的集群
     * @param appName 锁前缀
     * @param lockKey 锁的唯一标识
     * @param requestId 可以手动指定requestId，也可以手动传到threadlocal里，相当于锁重入
     * @param timeout 获取锁的超时时间，-1表示一直等待，直到获取到锁，单位秒
     * @param timeOutThenExecBussiness 如果timeOut大于0，加锁超时后是返回加锁失败（timeOutThenRelease=false）还是执行业务（timeOutThenRelease=true）
     * @return 超时时间为-1：得到锁，返回true，否则一直阻塞，直到获取锁返回true；<br/>
     * 超时时间大于0：得到锁，返回true，得不到锁，等待一段时间，如果能得到，返回true，否则返回false；<br/>
     * 超时时间为0：得到锁，返回true，得不到锁，返回false
     * @throws AddLockException 加锁失败
     */
	public static boolean tryGetDistributedLock(String prefixName, String appName, String lockKey, String requestId, int timeout, boolean timeOutThenExecBussiness) throws AddLockException {
		return lockOperator.tryGetDistributedLock(prefixName, appName, lockKey, requestId, timeout, timeOutThenExecBussiness);
	}
	
	/**@param prefixName 当配置了多个集群时，可以指定使用哪个前缀的集群
	 * @param appName
	 * @param lockKey
	 * @param requestId
	 * @param timeout
	 * @param lockType 锁的实现方式，目前支持redis和zk
	 * @return
	 * @throws AddLockException
	 */
	public static boolean tryGetDistributedLock(String prefixName, String appName, String lockKey, String requestId, int timeout, String lockType) throws AddLockException {
		if(lockType == null) {
			return lockOperator.tryGetDistributedLock(prefixName, appName, lockKey, requestId, timeout, false);
		}
		LockOperator lockOperator = ExtensionLoader.getExtensionLoader(LockOperator.class).getExtension(lockType);
		return lockOperator.tryGetDistributedLock(prefixName, appName, lockKey, requestId, timeout, false);
	}
	
	/**
     * 释放分布式锁，直接使用redisUtil
     * @param prefixName 当配置了多个集群时，可以指定使用哪个前缀的集群
     * @param appName 锁前缀
     * @param lockKey 锁的唯一标识
     * @param release 最终是否释放锁
     * @return 如果能释放，返回true(不管是中途释放还是全部释放都是true)；没有锁，或者不是自己加的锁返回false
     * @throws ReleaseLockException 释放锁失败, UnrerentLockException 停止续租失败
     */
    public static boolean releaseDistributedLock(String prefixName, String appName, String lockKey, Boolean release) throws ReleaseLockException, UnrerentLockException {
		return lockOperator.releaseDistributedLock(prefixName, appName, lockKey, release, false);
	}
    
    /**@param prefixName 当配置了多个集群时，可以指定使用哪个前缀的集群
     * @param appName
     * @param lockKey
     * @param release
     * @param lockType 锁的实现方式，目前支持redis和zk
     * @return
     * @throws ReleaseLockException
     * @throws UnrerentLockException
     */
    public static boolean releaseDistributedLock(String prefixName, String appName, String lockKey, Boolean release, String lockType) throws ReleaseLockException, UnrerentLockException {
    	if(lockType == null) {
    		return lockOperator.releaseDistributedLock(prefixName, appName, lockKey, release, false);
    	}
    	LockOperator lockOperator = ExtensionLoader.getExtensionLoader(LockOperator.class).getExtension(lockType);
    	return lockOperator.releaseDistributedLock(prefixName, appName, lockKey, release, false);
    }
    
    /**注意：zk不支持
     * 获取threadlocal里的requestId和count，已经不建议使用，直接返回map第一个元素
     * @return
     */
    @Deprecated
    public static TwoTuple<String, Integer> getLockRequestIdAndCount() {
		return lockOperator.getLockRequestIdAndCount();
	}
    
    /**注意：zk不支持
     * 获取threadlocal里的requestId和count
     * @return
     */
    public static TwoTuple<String, Integer> getLockRequestIdAndCount(String lockKey) {
    	return lockOperator.getLockRequestIdAndCount(lockKey);
    }

}
